package dmxGroupControl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Timer;
import java.util.TimerTask;

import dmxGroupControl.transmitters.DMXTransmitter;
import dmxGroupControl.transmitters.PixelTransmitter;
import dmxGroupControl.transmitters.Transmitter;

public class Universe 
{
	int DMX_UPDATE_TIME = 25; // 40hz update rate, 25 ms wait
	Timer updateScheduler;

	ArrayList<Group> groups;
	ArrayList<Transmitter> transmitters;
	
	String name;
	
	String address;
	
	public Universe(String name)
    {
        groups = new ArrayList<>();
        transmitters = new ArrayList<>();
        
        TimerTask task = new TimerTask()
        {
            @Override
            public void run()
            {
                Thread.currentThread().setName("Universe Controller");
                if(LightController.AreUpdatesEnabled())
                {
                    Update();
                }
            }
        };
        
        updateScheduler = new Timer(true);
        updateScheduler.scheduleAtFixedRate(task, 0, DMX_UPDATE_TIME);
        
        this.name = name;
    }
	
	public ArrayList<Group> GetGroups()
	{
	    return groups;
	}
	
	public String GetAddress()
	{
	    return address;
	}
	
	public String GetName()
	{
	    return name;
	}
	
	public void close()
	{
	    for(Group group : groups)
	    {
	        group.close();
	    }
	    for(Transmitter transmitter : transmitters)
	    {
	    	transmitter.close();
	    }
	    updateScheduler.cancel();
	}
	
	private int GetSize()
    {
        int maximum = 0;
        for(Group group : groups)
        {
            for(DMXFixture fixture : group.GetDMXFixtures())
            {
                if(fixture.GetChannelEnd() > maximum)
                {
                    maximum = fixture.GetChannelEnd();
                }
            }
        }
        return maximum;
    }
	
	private byte[] GetDMXData()
	{
		HashMap<Integer, Integer> channelValues = new HashMap<>();
		
	    for(Group group : groups)
        {
            for(DMXFixture fixture : group.GetDMXFixtures())
            {
            	
                channelValues.putAll(fixture.GetDMXChannels());
            }
        }
	    
        byte[] data = new byte[GetSize()];

        for (int i = 0; i < GetSize(); i++)
        {
            if(channelValues.containsKey(i + 1))
            {
                data[i] = channelValues.get(i + 1).byteValue();
            }
            else
            {
                data[i] = 0;
            }
        }
        
        return data;
	}
	
	private byte[] GetPixelData()
	{
	    ArrayList<Byte> bytes = new ArrayList<>();
	    
		for(Group group : groups)
        {
            for(PixelFixture fixture : group.GetPixelFixtures())
            {
            	bytes.addAll(fixture.GetRGBData());
            }
        }
	    
		byte[] returnValue = new byte[bytes.size()];
		for(int i = 0; i < bytes.size(); i++)
		{
			returnValue[i] = bytes.get(i);
		}
        
        return returnValue;
	}
	
	@Override
	public String toString()
	{
		return GetName();
	}

	public void Update()
	{
	    for(Group group : groups)
        {
            group.Update();
        }
	    
	    byte[] DMXData = GetDMXData();
	    byte[] PixelData = GetPixelData();
	    
	    for(Transmitter transmitter : transmitters)
	    {
	    	if(transmitter instanceof DMXTransmitter)
	    	{
	    		((DMXTransmitter)transmitter).SendDMX(DMXData);
	    	}
	    	else if(transmitter instanceof PixelTransmitter)
	    	{
	    		((PixelTransmitter)transmitter).SendPixels(PixelData);
	    	}
	        
	    }
	}
    
	public ArrayList<Transmitter> GetTransmitters()
	{
	    return transmitters;
	}
	
	private <T> T Find(String needle, ArrayList<T> haystack)
	{
	    for(T item : haystack)
	    {
	        if(item.toString().compareTo(needle) == 0)
	        {
	            return item;
	        }
	    }
	    return null;
	}

	public Group FindGroup(String groupName)
	{
	    return Find(groupName, groups);
	}

    public Transmitter FindTransmitter(String transmitterName)
    {
        return Find(transmitterName, transmitters);
    }

    public void SetName(String value)
    {
        name = value;
    }
}
